#include "MetadataOrganization.h"
#include "ImposibleWriteFileException.h"
#include "ImposibleReadFileException.h"
#include "ImposibleOpenFileException.h"
#include "ImposibleCloseFileException.h"
#include "NotExistNameException.h"
#include "File.h"

#ifndef ZERO
	#define ZERO 0
#endif

#ifndef UNO
	#define UNO 1
#endif

#ifndef MENOS_UNO
	#define MENOS_UNO -1
#endif

#ifndef MAX_SIZE_NOMBRE
	#define MAX_SIZE_NOMBRE 256
#endif

#ifndef MAX_SIZE_KEY
	#define MAX_SIZE_KEY 4086
#endif

MetadataOrganization::MetadataOrganization()
	:handler(ZERO),
	name(""),
	status(ZERO),
	currentField(ZERO),
	currentIndex(ZERO),
	idIndexIterator(ZERO),
	currentFieldIndex(ZERO)
{
}

MetadataOrganization::MetadataOrganization(const MetadataOrganization &toCopy)
{
	handler = toCopy.handler;
	name = toCopy.name;
	status = toCopy.status;	
	currentField = toCopy.currentField;
	currentIndex = toCopy.currentIndex;
	idIndexIterator = toCopy.idIndexIterator;
	currentFieldIndex = toCopy.currentFieldIndex;
	fields = toCopy.fields;
	indexes = toCopy.indexes;
}

MetadataOrganization::~MetadataOrganization()
{
	handler = ZERO;
	name.clear();
	status = ZERO;
	currentField = ZERO;
	currentIndex = ZERO;
	idIndexIterator = ZERO;
	currentFieldIndex = ZERO;
	fields.clear();
	indexes.clear();
}

int MetadataOrganization::getMaxField()
{
	std::vector<Field>::iterator it = fields.begin();
	int max = ZERO;
	
	if(it != fields.end())
	{
		for(it = fields.begin(); it != fields.end(); it++)
		{
			if((*it).getID() > max)
				max = (*it).getID();
		}
		max++;
	}
	return max;
}

int MetadataOrganization::getMaxIndex()
{
	std::vector<Index>::iterator it = indexes.begin();
	int max = ZERO;
	
	if(it != indexes.end())
	{
		for(it = indexes.begin(); it != indexes.end(); it++)
		{
			if((*it).getID() > max)
				max = (*it).getID();
		}
		max++;
	}
	return max;
}

void MetadataOrganization::clear()
{
	handler = ZERO;
	//name.clear();
	status = ZERO;
	currentField = ZERO;
	currentIndex = ZERO;
	idIndexIterator = ZERO;
	currentFieldIndex = ZERO;
	fields.clear();
	indexes.clear();
}

int MetadataOrganization::getHandler()
{
	return handler;
}

void MetadataOrganization::setHandler(int manejador)
{
	handler = manejador;
}

void MetadataOrganization::getName(std::string &nombre)
{
	nombre = name;
}

void MetadataOrganization::setName(std::string nombre)
{
	name = nombre;
}

int MetadataOrganization::addField(campo * ptrCampo)
{
	Field field;
	int retorno = MENOS_UNO;

	if(ptrCampo != NULL)
	{
		retorno = this->getMaxField();
		field.set(retorno, ptrCampo);
		fields.push_back(field);
	}
	return retorno;
}

void MetadataOrganization::getField(int idField, Field *field)
{
	std::vector<Field>::iterator it = fields.begin();
	bool find = false;

	if(field != NULL)
	{
		while(it != fields.end() && !find)
		{
			if((*it).getID() == idField)
			{
				(*field) = (*it);
				find = true;
			}
			it++;
		}
	}
}

void MetadataOrganization::startFieldIterator()
{
	currentField = ZERO;
}

bool MetadataOrganization::hasNextField()
{
	return (currentField < fields.size());
}

void MetadataOrganization::nextField(Field * field)
{
	std::vector<Field>::iterator it = fields.begin();
	
	if(field != NULL && it != fields.end())
	{
		it += currentField;
		(*field) = (*it);
		currentField++;
	}
}

int MetadataOrganization::addIndex(campo * campos, std::string &indexName)
{
	Index index;
	Field field;
	unsigned int i = ZERO;
	int retorno = MENOS_UNO;
	int idField = ZERO;
	campo tempCampo;
	std::vector<Field>::iterator it = fields.begin();

	if(campos != NULL)
	{
		if(indexName != "")
			indexName.clear();
		retorno = this->getMaxIndex();
		index.setID(retorno);
		while(campos[i].nombre)
		{
			it = fields.begin();
			while(it != fields.end())
			{
				tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
				memset((void*)(tempCampo.nombre), 0, MAX_SIZE_NOMBRE);
				(*it).get(idField, &tempCampo);
				if(strcmp(tempCampo.nombre, campos[i].nombre) == ZERO)
				{
					field.set(idField, &tempCampo);
					index.addField(&field);
					indexName += tempCampo.nombre;
				}
				it++;
				delete [](tempCampo.nombre);
			}
			i++;
		}
	}
	if(index.getFieldCount() > ZERO)
		indexes.push_back(index);
	return retorno;
}

void MetadataOrganization::getIndex(int idIndex, Index *index)
{
	std::vector<Index>::iterator it = indexes.begin();
	bool find = false;

	if(index != NULL)
	{
	
		while(it != indexes.end() && !find)
		{
			if((*it).getID() == idIndex)
			{
				(*index) = (*it);
				find = true;
			}
			it++;
		}
	}
}

void MetadataOrganization::dropIndex(int idIndex, std::string &indexName)
{
	std::vector<Index>::iterator it = indexes.begin();
	bool find = false;
	unsigned int pos = ZERO;
	int idField = ZERO;
	Field tempField;
	Index tempIndex;
	campo tempCampo;

	if(indexName != "")
		indexName.clear();

	while(it != indexes.end() && !find)
	{
		if((*it).getID() == idIndex)
		{
			tempIndex = (*it);
			tempIndex.start();
			while(tempIndex.hasNext())
			{
				tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
				memset((void*)tempCampo.nombre, 0, MAX_SIZE_NOMBRE);
				idField = tempIndex.next();
				this->getField(idField, &tempField);
				tempField.get(idField, &tempCampo);
				indexName += tempCampo.nombre;
				delete [](tempCampo.nombre);
			}
			find = true;
		}
		else
			pos++;
		it++;
	}
	if(find)
	{
		it = indexes.begin() + pos;
		indexes.erase(it);
	}
}

bool MetadataOrganization::isIndexByID(int idIndex)
{
	std::vector<Index>::iterator it = indexes.begin();
	bool find = false;

	while(it != indexes.end() && !find)
	{
		if((*it).getID() == idIndex)
			find = true;
		it++;
	}
	return find;
}

bool MetadataOrganization::isIndex(campo *campos, int &idIndex)
{
	std::vector<Index>::iterator it = indexes.begin();
	bool find = false;
	bool matching = false;
	int idField = ZERO;
	unsigned int pos = ZERO;
	Field tempField;
	Index tempIndex;
	campo tempCampo;

	idIndex = ZERO;
	while(it != indexes.end() && !find)
	{
		tempIndex = (*it);
		tempIndex.start();
		pos = ZERO;
		//while(tempIndex.hasNext() && matching && campos[pos].nombre)
		while(tempIndex.hasNext() && !matching)
		{
			tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
			memset((void*)tempCampo.nombre, 0, MAX_SIZE_NOMBRE);
			idField = tempIndex.next();
			this->getField(idField, &tempField);
			tempField.get(idField, &tempCampo);
			if(strcmp(tempCampo.nombre, campos[pos].nombre) == ZERO)
				matching = true;
			pos++;
			delete [](tempCampo.nombre);
		}
		if(matching)
		{
			idIndex = (*it).getID();
			find = true;
		}
		it++;
	}
	if(find)
		return true;
	else
		return false;
}

void MetadataOrganization::getIndexName(int idIndex, std::string &indexName)
{
	std::vector<Index>::iterator it = indexes.begin();
	bool find = false;
	unsigned int pos = ZERO;
	int idField = ZERO;
	Field tempField;
	Index tempIndex;
	campo tempCampo;

	if(indexName != "")
		indexName.clear();

	while(it != indexes.end() && !find)
	{
		if((*it).getID() == idIndex)
		{
			tempIndex = (*it);
			tempIndex.start();
			while(tempIndex.hasNext())
			{
				tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
				memset((void*)tempCampo.nombre, 0, MAX_SIZE_NOMBRE);
				idField = tempIndex.next();
				this->getField(idField, &tempField);
				tempField.get(idField, &tempCampo);
				indexName += tempCampo.nombre;
				delete [](tempCampo.nombre);
			}
			find = true;
		}
		else
			pos++;
		it++;
	}
}

void MetadataOrganization::startIndexIterator()
{
	currentIndex = ZERO;
}

bool MetadataOrganization::hasNextIndex()
{
	return (currentIndex < indexes.size());
}

int MetadataOrganization::nextIndex()
{
	std::vector<Index>::iterator it = indexes.begin();
	int retorno = ZERO;
	it += currentIndex;
	retorno = (*it).getID();
	currentIndex++;
	return retorno;
}

void MetadataOrganization::startFieldIndexIterator(int idIndex)
{
	currentFieldIndex = ZERO;
	idIndexIterator = idIndex;
}

bool MetadataOrganization::hasNextFieldIndex()
{
	Index index;

	this->getIndex(idIndexIterator, &index);

	return (currentFieldIndex < index.getFieldCount());
}

int MetadataOrganization::nextFieldIndex()
{
	Index index;
	int retorno = ZERO;

	this->getIndex(idIndexIterator, &index);
	retorno = index.getIdField(currentFieldIndex);
	currentFieldIndex++;
	return retorno;
}


void MetadataOrganization::TakeKey(void* reg, int IdField, char* key, unsigned int &size)
{
	bool find = false;
	Field field;
	campo tempCampo;
	int id = ZERO;
	char *current = (char*)((char*)reg + sizeof(unsigned int));
	unsigned int sizeType = ZERO;
	int tipo = ZERO;
	
	// Toma la clave de un determinado registro de acuerdo al IdField
	if(reg != NULL)
	{
		this->startFieldIterator();
		
		while(this->hasNextField() && !find)
		{
			this->nextField(&field);

			tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
			memset((void*)tempCampo.nombre, 0, MAX_SIZE_NOMBRE);
			field.get(id, &tempCampo);

			if(tempCampo.longitud > ZERO)
			{
				tipo = tempCampo.tipo & ~UNSIGNED;
				switch(tipo)
				{
						case CHAR: sizeType = tempCampo.longitud; break;
						case INT: sizeType = sizeof(int); break;
						case LONG: sizeType = sizeof(long int); break;
						case FLOAT: sizeType = sizeof(float); break;
						case DOUBLE: sizeType = sizeof(double); break;
				};
				
				if(id == IdField)
				{
					memcpy((char*)key, current, sizeType);
					size = sizeType;
					find = true;
				}
				// Avanzo al siguiente campo
				current += sizeType;
			}

			delete []tempCampo.nombre;
		}
	}
	
}


void MetadataOrganization::GetKey(void *reg, int idIndex, char * key, unsigned int &size)
{
	Index index;
	Field tempField;
	int idField = ZERO;
	char* current = key;
	char currentKey[MAX_SIZE_KEY];
	unsigned int tempSize = ZERO;
	unsigned int total = ZERO;
	

	// Toma la clave de un determinado registro de acuerdo al IdField
	if(reg != NULL)
	{
		memset(currentKey, ZERO, MAX_SIZE_KEY);

		this->startFieldIndexIterator(idIndex);

		while(this->hasNextFieldIndex())
		{
			idField = this->nextFieldIndex();

			this->TakeKey(reg, idField, currentKey, tempSize);

			memcpy(current, currentKey, tempSize);

			current += tempSize;
			total += tempSize;
		}
		size = total;
	}
}

void MetadataOrganization::load()
{
	File *file = NULL;
	campo tempCampo;
	int idField = ZERO;
	int idFieldIndex = ZERO;
	int idIndex = ZERO;
	int tipo = ZERO;
	unsigned int i = ZERO, e = ZERO;
	unsigned int sizeNombre = ZERO;
	int longitudCampo = ZERO;
	unsigned int sizeFields = ZERO;
	unsigned int sizeIndexes = ZERO;
	unsigned int sizeFieldIndex = ZERO;
	char nombre[MAX_SIZE_NOMBRE];
	
	try
	{
		if(name == "")
		{	
			throw NotExistNameException ();
		}

		file = new File();
		this->clear();

//		file = new File();

		if(file->open(name.c_str())==F_ERROR)
		{
			throw ImposibleOpenFileException ();
		}

		file->read((char*)&handler, sizeof(int));
		file->read((char*)&status, sizeof(int));
		file->read((char*)&currentField, sizeof(unsigned int));
		file->read((char*)&currentIndex, sizeof(unsigned int));
		file->read((char*)&idIndexIterator, sizeof(int));
		file->read((char*)&currentFieldIndex, sizeof(unsigned int));

		file->read((char*)&sizeFields, sizeof(unsigned int));

		tempCampo.nombre = new char[MAX_SIZE_NOMBRE];

		for(i = ZERO; i < sizeFields; i++)
		{
			file->read((char*)&idField, sizeof(int));
			file->read((char*)&sizeNombre, sizeof(unsigned int));
			file->read((char*)nombre, sizeNombre);
			file->read((char*) &tipo, sizeof(int));
			file->read((char*) &longitudCampo, sizeof(int));
			if(tempCampo.nombre != NULL)
				delete [](tempCampo.nombre);			
			tempCampo.nombre = new char[sizeNombre + UNO];
			memset((char*)tempCampo.nombre, 0, sizeNombre + UNO);
			memcpy((char*)tempCampo.nombre, nombre, sizeNombre);
			tempCampo.tipo = tipo;
			tempCampo.longitud = longitudCampo;

			this->addField(&tempCampo);
		}

		if(tempCampo.nombre != NULL)
				delete [](tempCampo.nombre);
	
		file->read((char*)&sizeIndexes, sizeof(unsigned int));
		
		for(i = ZERO; i < sizeIndexes; i++)
		{
			Index index;
			Field field;

			file->read((char*)&sizeFieldIndex, sizeof(unsigned int));

			for( e = ZERO; e < sizeFieldIndex; e++)
			{
				file->read((char*)&idFieldIndex, sizeof(int));
				field.set(idFieldIndex, NULL);
				index.addField(&field);
			}
			index.setID(idIndex);
			indexes.push_back(index);
			idIndex++;
		}

		if(file->close() == RES_ERROR)
		{
			throw ImposibleCloseFileException ();
		}

		delete file;
	}
	catch ( NotExistNameException & ex1)
	{
		throw NotExistNameException ();
	}
	catch ( ImposibleOpenFileException & ex2)
	{
		throw ImposibleOpenFileException ();
	}
	catch ( ImposibleCloseFileException  & ex3)
	{
		throw ImposibleCloseFileException ();
	}
	catch ( ImposibleReadFileException  & ex3)
	{
		throw ImposibleReadFileException ();
	}
}

void MetadataOrganization::save()
{
	File *file = NULL;
	campo tempCampo;
	Index index;
	int idField = ZERO;
	int idFieldIndex = ZERO;
	int tipo = ZERO;
	unsigned int sizeNombre = ZERO;
	int longitudCampo = ZERO;
	unsigned int sizeFields = ZERO;
	unsigned int sizeIndexes = ZERO;
	unsigned int sizeFieldIndex = ZERO;
	
	try
	{
		if(name == "")
		{	
			throw NotExistNameException ();
		}
		
		file = new File();
		
		if((file->open(name.c_str()))==F_ERROR)
		{
			throw ImposibleOpenFileException ();
		}
		

		file->write((char*)&handler, sizeof(int));
		file->write((char*)&status, sizeof(int));
		file->write((char*)&currentField, sizeof(unsigned int));
		file->write((char*)&currentIndex, sizeof(unsigned int));
		file->write((char*)&idIndexIterator, sizeof(int));
		file->write((char*)&currentFieldIndex, sizeof(unsigned int));

		sizeFields = fields.size();
		file->write((char*)&sizeFields, sizeof(unsigned int));

		for(std::vector<Field>::iterator it = fields.begin(); it != fields.end(); it++)
		{
			tempCampo.nombre = new char[MAX_SIZE_NOMBRE];
			memset((void*)tempCampo.nombre, 0, MAX_SIZE_NOMBRE);
			(*it).get(idField, &tempCampo);
			file->write((char*)&idField, sizeof(int));
			sizeNombre = strlen(tempCampo.nombre);
			file->write((char*)&sizeNombre, sizeof(unsigned int));
			file->write((char*)tempCampo.nombre, sizeNombre);
			tipo = tempCampo.tipo;
			file->write((char*) &tipo, sizeof(int));
			longitudCampo = tempCampo.longitud;
			file->write((char*) &longitudCampo, sizeof(int));
			delete [](tempCampo.nombre);
		}
	
		sizeIndexes = indexes.size();
		file->write((char*)&sizeIndexes, sizeof(unsigned int));
		
		for(std::vector<Index>::iterator it = indexes.begin(); it != indexes.end(); it++)
		{
			index = (*it);
			sizeFieldIndex = index.getFieldCount();
			file->write((char*)&sizeFieldIndex , sizeof(unsigned int));
			
			index.start();
			while(index.hasNext())
			{
				idFieldIndex = index.next();
				file->write((char*)&idFieldIndex, sizeof(int));
			}
		}

		if(file->close() == RES_ERROR)
		{
			throw ImposibleCloseFileException ();
		}
		delete file;
	}
	catch ( NotExistNameException & ex1)
	{
		if(file != NULL)
			delete file;
		throw NotExistNameException ();
	}
	catch ( ImposibleOpenFileException & ex2)
	{
		if(file != NULL)
			delete file;
		throw ImposibleOpenFileException ();
	}
	catch ( ImposibleCloseFileException  & ex3)
	{
		if(file != NULL)
			delete file;
		throw ImposibleCloseFileException ();
	}
	catch ( ImposibleWriteFileException  & ex3)
	{
		if(file != NULL)
			delete file;
		throw ImposibleWriteFileException ();
	}
}

const MetadataOrganization &MetadataOrganization::operator=(const MetadataOrganization &toAssign)
{
	if(this != &toAssign)
	{
		handler = toAssign.handler;
		name = toAssign.name;
		status = toAssign.status;	
		currentField = toAssign.currentField;
		currentIndex = toAssign.currentIndex;
		idIndexIterator = toAssign.idIndexIterator;
		currentFieldIndex = toAssign.currentFieldIndex;
		fields = toAssign.fields;
		indexes = toAssign.indexes;
	}
	return (*this);
}


